package com.ruoyi.learn.java.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.UUID;

public class ThreadLocalLeakDemo {

    // 模拟一个大对象（比如用户上下文、数据库连接等）
    static class LargeObject {
        private byte[] data = new byte[1024 * 1024]; // 1MB
        private String id = UUID.randomUUID().toString();

        @Override
        public String toString() {
            return "LargeObject{" + "id='" + id + '\'' + '}';
        }
    }

    // ThreadLocal 存储大对象
//    static ThreadLocal<LargeObject> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        // 创建一个固定大小的线程池（2 个线程，长期运行）
        ExecutorService executor = Executors.newFixedThreadPool(6);

        System.out.println("开始提交任务...");

        // 提交 1000 个任务
        for (int i = 0; i < 60000; i++) {
            final int taskId = i;
            executor.submit(() -> {
                ThreadLocal<LargeObject> threadLocal = new ThreadLocal<>();
                // 每个任务都设置一个大对象到 ThreadLocal
                threadLocal.set(new LargeObject());

                // 模拟业务处理（不清理）
                try {
                    Thread.sleep(100); // 简单休眠
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                // ❌ 错误：没有调用 threadLocal.remove()
                // 导致：LargeObject 被 Entry.value 强引用，无法回收
                System.out.println("任务 " + taskId + " 执行完毕");
            });
        }

        // 关闭线程池
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.SECONDS);

        System.out.println("任务全部提交并执行完毕。");

        // ⚠️ 问题：线程池中的线程仍然存活，它们的 ThreadLocalMap 中
        // 仍然持有 1000 个 LargeObject 的引用（虽然每个线程只保留最后一个？）
        // 实际上：由于 ThreadLocal 是 static，每个线程的 ThreadLocalMap 只有一个 Entry，
        // 但 value 会被不断替换。但如果 ThreadLocal 很多，或 value 未及时清理，就会泄漏。

        // 为了更明显地展示泄漏，我们可以让主线程休眠，观察内存
        System.out.println("主线程休眠 30 秒，观察内存...");
        Thread.sleep(30_00000); // 此时可以用 jvisualvm 或 jconsole 观察内存
    }
}
